A look at the latest Jedlovec House eletricity usage and solar
production from PPL and Enphase.
Load packages
library(tidyverse)
library(lubridate)
library(hms)
library(readxl)
Load PPL data
Transform PPL Data
hourly_ppl_pivot <- ppl_15mins %>%
rename(date = Date) %>%
pivot_longer(!c("Account Number", "Meter Number", date, "Read Type", Min, Max, Total), names_to = "time", values_to = "kWh")
rename(ppl_15mins, date = Date)
hourly_ppl_pivot <- hourly_ppl_pivot %>%
mutate(time = parse_time(time, '%H:%M %p'), month = month(date, label=TRUE), year = year(date), yday = yday(date), wday = wday(date, label=TRUE))
(hourly_ppl_net <- hourly_ppl_pivot %>%
filter(`Read Type` == "kWh Net"))
Load Enphase data
import <- read_csv("enphase_history_230701.csv")
Rows: 42432 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): Date/Time
dbl (1): Energy Produced (Wh)
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
update <- read_csv("hourly_generation_230625_240510.csv")
Rows: 30816 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): Date/Time
dbl (1): Energy Produced (Wh)
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
update <- update %>%
mutate(`Date/Time` = as.POSIXct(`Date/Time`,format="%m/%d/%Y %H:%M",tz=Sys.timezone())) %>%
filter(`Date/Time` >= as.POSIXct('07/01/2023 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) )
import <- import %>%
mutate(`Date/Time` = as.POSIXct(`Date/Time`,format="%m/%d/%Y %H:%M",tz=Sys.timezone()))
full_hist <- rbind(import,update)
full_hist %>% arrange(desc(`Date/Time`))
(hourly_production <- full_hist %>%
rename(datetime = `Date/Time`, energy_produced_Wh = `Energy Produced (Wh)`) %>%
mutate(datetime = as.POSIXct(datetime,format="%m/%d/%Y %H:%M",tz=Sys.timezone())) %>%
mutate(date = date(datetime), time = as.hms(format(datetime, format = "%H:%M:%S")), month = month(datetime, label=TRUE), year = year(datetime), day = day(datetime), yday = yday(datetime), monthday = format(datetime, "%m-%d"), wday = wday(datetime, label=TRUE), equinox_day = (yday + 10) %% 365, equinox_group = floor((equinox_day+15)/30)*30)
)
NA
Spot-check Enphase Should see lifetime production by day and by
hour
ggplot(hourly_production, aes(datetime, energy_produced_Wh)) +
geom_point()

ggplot(hourly_production, aes(time, energy_produced_Wh)) +
theme(axis.text.x = element_text(angle = 90)) +
geom_point()

Net + Produced = Consumed
Calculate production for first year of solar panels, second year,
etc.
hourly_electricity <- hourly_electricity %>%
mutate(solar_year = factor(case_when(
datetime < as.POSIXct('04/16/2022 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ~ 0,
datetime >= as.POSIXct('04/16/2022 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) &
datetime < as.POSIXct('04/16/2023 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ~ 1,
datetime >= as.POSIXct('04/16/2023 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) &
datetime < as.POSIXct('04/16/2024 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ~ 2,
TRUE ~ 3)
)) %>%
mutate(yday = yday(date))
hourly_electricity %>%
group_by(solar_year) %>%
summarize(net_kWh = sum(net_kWh), produced_kWh = sum(produced_kWh), consumed_kWh = sum(consumed_kWh))
NA
Interesting! Produced kWh went down by 5%, but consumed kWh went down
by 10% despite the fact that we got an electric car! Let’s explore that
further.
daily_electricity <- hourly_electricity %>%
group_by(solar_year, yday) %>%
summarize(net_kWh = sum(net_kWh), produced_kWh = sum(produced_kWh), consumed_kWh = sum(consumed_kWh))
`summarise()` has grouped output by 'solar_year'. You can override using the `.groups` argument.
ggplot(daily_electricity, aes(yday, consumed_kWh, group=solar_year, color=solar_year)) +
geom_point() +
geom_smooth(span=0.3) +
scale_x_continuous(breaks = c(1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335), labels = month.abb) +
theme(axis.text.x = element_text(angle = 90))


Look how our electricity consumption got much less predictable, more
uneven, after we purchased an EV and installed a level 2 charger in late
January/early February 2023!
We probably need to filter out the EV charging and compare that
separately.
hourly_electricity %>% summarize(min_date = min(date), max_date = max(date))
electricity_by_time <- hourly_electricity %>%
filter(solar_year == 1 | solar_year == 2) %>%
#filter(date <= as.POSIXct('12/31/2022 00:00',format="%m/%d/%Y %H:%M",tz=Sys.timezone()) ) %>%
group_by(time, solar_year) %>%
summarize(produced_kWh = mean(produced_kWh), consumed_kWh = mean(consumed_kWh), net_kWh = mean(net_kWh)) %>%
arrange(time)
`summarise()` has grouped output by 'time'. You can override using the `.groups` argument.
electricity_by_time
ggplot(electricity_by_time, aes(x=time, y=consumed_kWh, group=solar_year, color=solar_year)) +
geom_point()

#labs(colour="",x="Time of Day",y="Electricity Consumption (kWh)")+
#scale_color_manual(values = c("red","black","green")) +
#ggtitle("Home Electricity in 15-Minute Intervals (Since April 15, 2022)")
Wow, look how much less was used during the day in year 2!!! Like a
50% reduction!
Is this due to milder weather?
Let’s look at it by month:
#electricity_by_month <-
hourly_electricity %>%
mutate(month = month(date)) %>%
group_by(solar_year, month) %>%
summarize(consumed_kWh = mean(consumed_kWh)) %>%
arrange(month, solar_year) %>%
pivot_wider(names_from = month, values_from = consumed_kWh) %>%
arrange(solar_year)
`summarise()` has grouped output by 'solar_year'. You can override using the `.groups` argument.
#Look at limited window during day (no EV charging)
# hourly_electricity %>%
# filter(time == '12:00:00') %>%
# #filter(time > as.POSIXct('04:00:00',format="%H:%M:$s",tz=Sys.timezone()) ) %>% #& time < as.POSIXct('18:00',format="%H:%M",tz=Sys.timezone())) %>%
# mutate(month = month(date)) %>%
# group_by(solar_year, month) %>%
# summarize(consumed_kWh = mean(consumed_kWh)) %>%
# arrange(month, solar_year) %>%
# pivot_wider(names_from = month, values_from = consumed_kWh) %>%
# arrange(solar_year)
Look at specific months
electricity_by_month_time <- hourly_electricity %>%
mutate(month = as_factor(month(date))) %>%
filter(solar_year == 1 | solar_year == 2) %>%
filter(month == 10) %>%
group_by(solar_year, month, time) %>%
summarize(produced_kWh = mean(produced_kWh), consumed_kWh = mean(consumed_kWh), net_kWh = mean(net_kWh)) %>%
arrange(solar_year, month, time)
`summarise()` has grouped output by 'solar_year', 'month'. You can override using the `.groups` argument.
ggplot(electricity_by_month_time, aes(time, consumed_kWh, group=solar_year, color=solar_year)) +
#facet_grid(rows = vars(month)) +
geom_point(size=0.5) +
ggtitle("Home Electricity in 15-Minute Intervals") +
ylab("Net Electricity Consumption (kWh)")

Example day with EV charging
sample_day <- hourly_electricity %>%
filter(solar_year == 1 | solar_year == 2) %>%
filter(yday == 157) %>%
group_by(date, solar_year, yday, time) %>%
summarize(produced_kWh = mean(produced_kWh), consumed_kWh = mean(consumed_kWh), net_kWh = mean(net_kWh)) %>%
arrange(date, solar_year, yday, time)
`summarise()` has grouped output by 'date', 'solar_year', 'yday'. You can override using the `.groups` argument.
ggplot(sample_day, aes(time, consumed_kWh, group=solar_year, color=solar_year)) +
#facet_grid(rows = vars(month)) +
geom_point(size=0.5) +
ggtitle("Home Electricity in 15-Minute Intervals") +
ylab("Electricity Consumption (kWh)")

Ok, let’s detect and remove EV charging sessions.
bcp
$x
[1] -0.4717343 0.7711507 -1.5990938 -0.7508534 0.8046654 -0.6484233 0.1813083 0.7249717 -0.6922743
[10] 1.0614834 3.6704000 4.0869019 4.0584831 4.9843196 5.1803859 5.1696722 5.0958858 5.5062540
[19] 5.3078164 5.3926576
$max_p
[,1] [,2] [,3]
[1,] 0.9009547 0 0
[2,] 0.8015196 0 1
[3,] 0.7616943 0 2
[4,] 0.7018043 0 3
[5,] 0.6985363 0 4
[6,] 0.6588125 0 5
[7,] 0.5838624 0 6
[8,] 0.6049459 0 7
[9,] 0.5252828 0 8
[10,] 0.3619282 0 9
[11,] 0.4815652 0 10
[12,] 0.5369016 10 0
[13,] 0.9141713 10 1
[14,] 0.9182526 10 2
[15,] 0.9211320 10 3
[16,] 0.9242694 10 4
[17,] 0.9249073 10 5
[18,] 0.9268006 10 6
[19,] 0.9281916 10 7
[20,] 0.0000000 0 0
$parameters
theta alpha beta th_cp
0.9 1.0 1.0 0.5
$series_length
[1] 20
$result
NULL
attr(,"class")
[1] "BayesCP"
LS0tCnRpdGxlOiAiRWxlY3RyaWNpdHkvU29sYXIgVXBkYXRlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpBIGxvb2sgYXQgdGhlIGxhdGVzdCBKZWRsb3ZlYyBIb3VzZSBlbGV0cmljaXR5IHVzYWdlIGFuZCBzb2xhciBwcm9kdWN0aW9uIGZyb20gUFBMIGFuZCBFbnBoYXNlLgoKTG9hZCBwYWNrYWdlcwpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGhtcykKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkob25saW5lQmNwKQpgYGAKCkxvYWQgUFBMIGRhdGEKYGBge3J9Cgpjb2xfZGF0YXR5cGVzIDwtIGMoJ251bWVyaWMnLCdudW1lcmljJywnZGF0ZScsJ3RleHQnLHJlcCgnbnVtZXJpYycsOTkpKQoKaG91cmx5MSA8LSByZWFkX2V4Y2VsKCJIb3VybHkgVXNhZ2UgMjIwMzI2IHRvIDIyMDYyNC54bHN4IiwgY29sX3R5cGVzID0gY29sX2RhdGF0eXBlcykKaG91cmx5MiA8LSByZWFkX2V4Y2VsKCJIb3VybHkgVXNhZ2UgMjIwNjI1IHRvIDIzMDYyMy54bHN4IiwgY29sX3R5cGVzID0gY29sX2RhdGF0eXBlcykKaG91cmx5MyA8LSByZWFkX2V4Y2VsKCJQUEwgMjMwNjI0IHRvIDI0MDUwOS54bHN4IiwgY29sX3R5cGVzID0gY29sX2RhdGF0eXBlcykKCiNob3VybHkxCiNob3VybHkyCiNob3VybHkzCgpwcGxfMTVtaW5zIDwtIGJpbmRfcm93cyhob3VybHkxLGxpc3QoaG91cmx5Mixob3VybHkzKSkKCnBwbF8xNW1pbnMgJT4lIGFycmFuZ2UoZGVzYyhEYXRlKSwgYFJlYWQgVHlwZWApCgoKYGBgCgpUcmFuc2Zvcm0gUFBMIERhdGEKYGBge3J9Cgpob3VybHlfcHBsX3Bpdm90IDwtIHBwbF8xNW1pbnMgJT4lIAogIHJlbmFtZShkYXRlID0gRGF0ZSkgJT4lIAogIHBpdm90X2xvbmdlcighYygiQWNjb3VudCBOdW1iZXIiLCAiTWV0ZXIgTnVtYmVyIiwgZGF0ZSwgIlJlYWQgVHlwZSIsIE1pbiwgTWF4LCBUb3RhbCksIG5hbWVzX3RvID0gInRpbWUiLCB2YWx1ZXNfdG8gPSAia1doIikgCgpyZW5hbWUocHBsXzE1bWlucywgZGF0ZSA9IERhdGUpCgpob3VybHlfcHBsX3Bpdm90IDwtIGhvdXJseV9wcGxfcGl2b3QgJT4lIAogIG11dGF0ZSh0aW1lID0gcGFyc2VfdGltZSh0aW1lLCAnJUg6JU0gJXAnKSwgbW9udGggPSBtb250aChkYXRlLCBsYWJlbD1UUlVFKSwgeWVhciA9IHllYXIoZGF0ZSksIHlkYXkgPSB5ZGF5KGRhdGUpLCB3ZGF5ID0gd2RheShkYXRlLCBsYWJlbD1UUlVFKSkKCihob3VybHlfcHBsX25ldCA8LSBob3VybHlfcHBsX3Bpdm90ICU+JSAKICBmaWx0ZXIoYFJlYWQgVHlwZWAgPT0gImtXaCBOZXQiKSkKYGBgCgpMb2FkIEVucGhhc2UgZGF0YQpgYGB7cn0KaW1wb3J0IDwtIHJlYWRfY3N2KCJlbnBoYXNlX2hpc3RvcnlfMjMwNzAxLmNzdiIpCnVwZGF0ZSA8LSByZWFkX2NzdigiaG91cmx5X2dlbmVyYXRpb25fMjMwNjI1XzI0MDUxMC5jc3YiKQoKdXBkYXRlIDwtIHVwZGF0ZSAlPiUgCiAgbXV0YXRlKGBEYXRlL1RpbWVgID0gYXMuUE9TSVhjdChgRGF0ZS9UaW1lYCxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkpICU+JSAKICBmaWx0ZXIoYERhdGUvVGltZWAgPj0gYXMuUE9TSVhjdCgnMDcvMDEvMjAyMyAwMDowMCcsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpICkKCmltcG9ydCA8LSBpbXBvcnQgJT4lIAogIG11dGF0ZShgRGF0ZS9UaW1lYCA9IGFzLlBPU0lYY3QoYERhdGUvVGltZWAsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpKSAKCmZ1bGxfaGlzdCA8LSByYmluZChpbXBvcnQsdXBkYXRlKQoKZnVsbF9oaXN0ICU+JSBhcnJhbmdlKGRlc2MoYERhdGUvVGltZWApKQoKKGhvdXJseV9wcm9kdWN0aW9uIDwtIGZ1bGxfaGlzdCAlPiUgCiAgcmVuYW1lKGRhdGV0aW1lID0gYERhdGUvVGltZWAsIGVuZXJneV9wcm9kdWNlZF9XaCA9IGBFbmVyZ3kgUHJvZHVjZWQgKFdoKWApICU+JSAKICBtdXRhdGUoZGF0ZXRpbWUgPSBhcy5QT1NJWGN0KGRhdGV0aW1lLGZvcm1hdD0iJW0vJWQvJVkgJUg6JU0iLHR6PVN5cy50aW1lem9uZSgpKSkgJT4lIAogIG11dGF0ZShkYXRlID0gZGF0ZShkYXRldGltZSksIHRpbWUgPSBhcy5obXMoZm9ybWF0KGRhdGV0aW1lLCBmb3JtYXQgPSAiJUg6JU06JVMiKSksIG1vbnRoID0gbW9udGgoZGF0ZXRpbWUsIGxhYmVsPVRSVUUpLCB5ZWFyID0geWVhcihkYXRldGltZSksIGRheSA9IGRheShkYXRldGltZSksIHlkYXkgPSB5ZGF5KGRhdGV0aW1lKSwgbW9udGhkYXkgPSBmb3JtYXQoZGF0ZXRpbWUsICIlbS0lZCIpLCB3ZGF5ID0gd2RheShkYXRldGltZSwgbGFiZWw9VFJVRSksIGVxdWlub3hfZGF5ID0gKHlkYXkgKyAxMCkgJSUgMzY1LCBlcXVpbm94X2dyb3VwID0gZmxvb3IoKGVxdWlub3hfZGF5KzE1KS8zMCkqMzApCikKCmBgYAoKU3BvdC1jaGVjayBFbnBoYXNlClNob3VsZCBzZWUgbGlmZXRpbWUgcHJvZHVjdGlvbiBieSBkYXkgYW5kIGJ5IGhvdXIKYGBge3J9CmdncGxvdChob3VybHlfcHJvZHVjdGlvbiwgYWVzKGRhdGV0aW1lLCBlbmVyZ3lfcHJvZHVjZWRfV2gpKSArCiAgZ2VvbV9wb2ludCgpCgpnZ3Bsb3QoaG91cmx5X3Byb2R1Y3Rpb24sIGFlcyh0aW1lLCBlbmVyZ3lfcHJvZHVjZWRfV2gpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICBnZW9tX3BvaW50KCkKCmBgYAoKTmV0ICsgUHJvZHVjZWQgPSBDb25zdW1lZAoKYGBge3J9CiMgaG91cmx5X3BwbF9uZXQgPC0gaG91cmx5X3BwbF9uZXQgJT4lIG11dGF0ZShkYXRlID0gYXNfZGF0ZShkYXRlKSkKCiNob3VybHlfcHBsX25ldCAlPiUgYXJyYW5nZShkZXNjKGRhdGUpKQojaG91cmx5X3Byb2R1Y3Rpb24gJT4lIGFycmFuZ2UoZGVzYyhkYXRlKSkKCihob3VybHlfZWxlY3RyaWNpdHkgPC0gaG91cmx5X3BwbF9uZXQgJT4lIAogICAgaW5uZXJfam9pbihob3VybHlfcHJvZHVjdGlvbiwgYnkgPSBqb2luX2J5KGRhdGUsdGltZSkpICAlPiUgCiAgICBtdXRhdGUoY29uc3VtZWRfa1doID0ga1doICsgZW5lcmd5X3Byb2R1Y2VkX1doLzEwMDAsIHByb2R1Y2VkX2tXaCA9IGVuZXJneV9wcm9kdWNlZF9XaC8xMDAwKSAgJT4lIAogICAgcmVuYW1lKG5ldF9rV2ggPSBrV2gpICU+JSAKICAgIHNlbGVjdChkYXRldGltZSwgZGF0ZSwgdGltZSwgbmV0X2tXaCwgcHJvZHVjZWRfa1doLCBjb25zdW1lZF9rV2gpICU+JQogICAgYXJyYW5nZShkYXRlKSkKCmBgYAoKQ2FsY3VsYXRlIHByb2R1Y3Rpb24gZm9yIGZpcnN0IHllYXIgb2Ygc29sYXIgcGFuZWxzLCBzZWNvbmQgeWVhciwgZXRjLgoKYGBge3J9Cgpob3VybHlfZWxlY3RyaWNpdHkgPC0gaG91cmx5X2VsZWN0cmljaXR5ICU+JSAKICBtdXRhdGUoc29sYXJfeWVhciA9IGZhY3RvcihjYXNlX3doZW4oCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lIDwgYXMuUE9TSVhjdCgnMDQvMTYvMjAyMiAwMDowMCcsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpIH4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWUgPj0gYXMuUE9TSVhjdCgnMDQvMTYvMjAyMiAwMDowMCcsZm9ybWF0PSIlbS8lZC8lWSAlSDolTSIsdHo9U3lzLnRpbWV6b25lKCkpICAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWUgPCBhcy5QT1NJWGN0KCcwNC8xNi8yMDIzIDAwOjAwJyxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkgfiAxLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZSA+PSBhcy5QT1NJWGN0KCcwNC8xNi8yMDIzIDAwOjAwJyxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkgICYKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZSA8IGFzLlBPU0lYY3QoJzA0LzE2LzIwMjQgMDA6MDAnLGZvcm1hdD0iJW0vJWQvJVkgJUg6JU0iLHR6PVN5cy50aW1lem9uZSgpKSB+IDIsCiAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAzKQogICAgICAgICApKSAlPiUgCiAgbXV0YXRlKHlkYXkgPSB5ZGF5KGRhdGUpKQoKaG91cmx5X2VsZWN0cmljaXR5ICU+JSAKICBncm91cF9ieShzb2xhcl95ZWFyKSAlPiUgCiAgc3VtbWFyaXplKG5ldF9rV2ggPSBzdW0obmV0X2tXaCksIHByb2R1Y2VkX2tXaCA9IHN1bShwcm9kdWNlZF9rV2gpLCBjb25zdW1lZF9rV2ggPSBzdW0oY29uc3VtZWRfa1doKSkKCmBgYAoKSW50ZXJlc3RpbmchIFByb2R1Y2VkIGtXaCB3ZW50IGRvd24gYnkgNSUsIGJ1dCBjb25zdW1lZCBrV2ggd2VudCBkb3duIGJ5IDEwJSBkZXNwaXRlIHRoZSBmYWN0IHRoYXQgd2UgZ290IGFuIGVsZWN0cmljIGNhciEgTGV0J3MgZXhwbG9yZSB0aGF0IGZ1cnRoZXIuIAoKYGBge3J9CmRhaWx5X2VsZWN0cmljaXR5IDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgZ3JvdXBfYnkoc29sYXJfeWVhciwgeWRheSkgJT4lIAogIHN1bW1hcml6ZShuZXRfa1doID0gc3VtKG5ldF9rV2gpLCBwcm9kdWNlZF9rV2ggPSBzdW0ocHJvZHVjZWRfa1doKSwgY29uc3VtZWRfa1doID0gc3VtKGNvbnN1bWVkX2tXaCkpCgpnZ3Bsb3QoZGFpbHlfZWxlY3RyaWNpdHksIGFlcyh5ZGF5LCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoc3Bhbj0wLjMpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxLCAzMiwgNjAsIDkxLCAxMjEsIDE1MiwgMTgyLCAyMTMsIDI0NCwgMjc0LCAzMDUsIDMzNSksIGxhYmVscyA9IG1vbnRoLmFiYikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSAKCmBgYAoKCmBgYHtyfQpkYWlseV9lbGVjdHJpY2l0eSA8LSBob3VybHlfZWxlY3RyaWNpdHkgJT4lIAogIG11dGF0ZShzb2xhcl9kYXkgPSBpbnRlcnZhbChhc19kYXRlKGFzLlBPU0lYY3QoJzA0LzE1LzIwMjInLGZvcm1hdD0iJW0vJWQvJVkiLHR6PVN5cy50aW1lem9uZSgpKSksYXNfZGF0ZShkYXRlKSkgLyBkYXlzKDEpIAogICAgICAgICApICU+JSAKICBncm91cF9ieShkYXRlLCBzb2xhcl9kYXkpICU+JSAKICBzdW1tYXJpemUobmV0X2tXaCA9IHN1bShuZXRfa1doKSwgcHJvZHVjZWRfa1doID0gc3VtKHByb2R1Y2VkX2tXaCksIGNvbnN1bWVkX2tXaCA9IHN1bShjb25zdW1lZF9rV2gpKQoKZ2dwbG90KGRhaWx5X2VsZWN0cmljaXR5LCBhZXMoZGF0ZSwgY29uc3VtZWRfa1doKSkgKyAKICBnZW9tX3BvaW50KCkgKwogICNzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxLCAzMiwgNjAsIDkxLCAxMjEsIDE1MiwgMTgyLCAyMTMsIDI0NCwgMjc0LCAzMDUsIDMzNSksIGxhYmVscyA9IG1vbnRoLmFiYikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSAKCgpgYGAKTG9vayBob3cgb3VyIGVsZWN0cmljaXR5IGNvbnN1bXB0aW9uIGdvdCBtdWNoIGxlc3MgcHJlZGljdGFibGUsIG1vcmUgdW5ldmVuLCBhZnRlciB3ZSBwdXJjaGFzZWQgYW4gRVYgYW5kIGluc3RhbGxlZCBhIGxldmVsIDIgY2hhcmdlciBpbiBsYXRlIEphbnVhcnkvZWFybHkgRmVicnVhcnkgMjAyMyEKCldlIHByb2JhYmx5IG5lZWQgdG8gZmlsdGVyIG91dCB0aGUgRVYgY2hhcmdpbmcgYW5kIGNvbXBhcmUgdGhhdCBzZXBhcmF0ZWx5LiAKCgpgYGB7cn0KCiNob3VybHlfZWxlY3RyaWNpdHkgJT4lIHN1bW1hcml6ZShtaW5fZGF0ZSA9IG1pbihkYXRlKSwgbWF4X2RhdGUgPSBtYXgoZGF0ZSkpCgplbGVjdHJpY2l0eV9ieV90aW1lIDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgZmlsdGVyKHNvbGFyX3llYXIgPT0gMSB8IHNvbGFyX3llYXIgPT0gMikgJT4lIAogICNmaWx0ZXIoZGF0ZSA8PSBhcy5QT1NJWGN0KCcxMi8zMS8yMDIyIDAwOjAwJyxmb3JtYXQ9IiVtLyVkLyVZICVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkgKSAlPiUgCiAgZ3JvdXBfYnkodGltZSwgc29sYXJfeWVhcikgJT4lIAogIHN1bW1hcml6ZShwcm9kdWNlZF9rV2ggPSBtZWFuKHByb2R1Y2VkX2tXaCksIGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSwgbmV0X2tXaCA9IG1lYW4obmV0X2tXaCkpICU+JSAKICBhcnJhbmdlKHRpbWUpCgplbGVjdHJpY2l0eV9ieV90aW1lCgpnZ3Bsb3QoZWxlY3RyaWNpdHlfYnlfdGltZSwgYWVzKHg9dGltZSwgeT1jb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgZ2VvbV9wb2ludCgpIAogICNsYWJzKGNvbG91cj0iIix4PSJUaW1lIG9mIERheSIseT0iRWxlY3RyaWNpdHkgQ29uc3VtcHRpb24gKGtXaCkiKSsKICAjc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsImJsYWNrIiwiZ3JlZW4iKSkgKwogICNnZ3RpdGxlKCJIb21lIEVsZWN0cmljaXR5IGluIDE1LU1pbnV0ZSBJbnRlcnZhbHMgKFNpbmNlIEFwcmlsIDE1LCAyMDIyKSIpCgpgYGAKV293LCBsb29rIGhvdyBtdWNoIGxlc3Mgd2FzIHVzZWQgZHVyaW5nIHRoZSBkYXkgaW4geWVhciAyISEhIExpa2UgYSA1MCUgcmVkdWN0aW9uISAKCklzIHRoaXMgZHVlIHRvIG1pbGRlciB3ZWF0aGVyPyAKCkxldCdzIGxvb2sgYXQgaXQgYnkgbW9udGg6IAoKYGBge3J9CiNlbGVjdHJpY2l0eV9ieV9tb250aCA8LSAKaG91cmx5X2VsZWN0cmljaXR5ICU+JSAKICBtdXRhdGUobW9udGggPSBtb250aChkYXRlKSkgJT4lIAogIGdyb3VwX2J5KHNvbGFyX3llYXIsIG1vbnRoKSAlPiUgCiAgc3VtbWFyaXplKGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSkgJT4lIAogIGFycmFuZ2UobW9udGgsIHNvbGFyX3llYXIpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbW9udGgsIHZhbHVlc19mcm9tID0gY29uc3VtZWRfa1doKSAlPiUgCiAgYXJyYW5nZShzb2xhcl95ZWFyKQoKI0xvb2sgYXQgbGltaXRlZCB3aW5kb3cgZHVyaW5nIGRheSAobm8gRVYgY2hhcmdpbmcpCiAKIyBob3VybHlfZWxlY3RyaWNpdHkgJT4lIAojICAgZmlsdGVyKHRpbWUgPT0gJzEyOjAwOjAwJykgJT4lIAojICAgI2ZpbHRlcih0aW1lID4gYXMuUE9TSVhjdCgnMDQ6MDA6MDAnLGZvcm1hdD0iJUg6JU06JHMiLHR6PVN5cy50aW1lem9uZSgpKSApICU+JSAgIyYgdGltZSA8IGFzLlBPU0lYY3QoJzE4OjAwJyxmb3JtYXQ9IiVIOiVNIix0ej1TeXMudGltZXpvbmUoKSkpICU+JSAKIyAgIG11dGF0ZShtb250aCA9IG1vbnRoKGRhdGUpKSAlPiUgCiMgICBncm91cF9ieShzb2xhcl95ZWFyLCBtb250aCkgJT4lIAojICAgc3VtbWFyaXplKGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSkgJT4lIAojICAgYXJyYW5nZShtb250aCwgc29sYXJfeWVhcikgJT4lIAojICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG1vbnRoLCB2YWx1ZXNfZnJvbSA9IGNvbnN1bWVkX2tXaCkgJT4lIAojICAgYXJyYW5nZShzb2xhcl95ZWFyKQoKCmBgYAoKTG9vayBhdCBzcGVjaWZpYyBtb250aHMKCmBgYHtyfQplbGVjdHJpY2l0eV9ieV9tb250aF90aW1lIDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgbXV0YXRlKG1vbnRoID0gYXNfZmFjdG9yKG1vbnRoKGRhdGUpKSkgJT4lIAogIGZpbHRlcihzb2xhcl95ZWFyID09IDEgfCBzb2xhcl95ZWFyID09IDIpICU+JSAKICBmaWx0ZXIobW9udGggPT0gMTApICU+JSAKICBncm91cF9ieShzb2xhcl95ZWFyLCBtb250aCwgdGltZSkgJT4lIAogIHN1bW1hcml6ZShwcm9kdWNlZF9rV2ggPSBtZWFuKHByb2R1Y2VkX2tXaCksIGNvbnN1bWVkX2tXaCA9IG1lYW4oY29uc3VtZWRfa1doKSwgbmV0X2tXaCA9IG1lYW4obmV0X2tXaCkpICU+JSAKICBhcnJhbmdlKHNvbGFyX3llYXIsIG1vbnRoLCB0aW1lKQoKZ2dwbG90KGVsZWN0cmljaXR5X2J5X21vbnRoX3RpbWUsIGFlcyh0aW1lLCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgI2ZhY2V0X2dyaWQocm93cyA9IHZhcnMobW9udGgpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNSkgKwogIGdndGl0bGUoIkhvbWUgRWxlY3RyaWNpdHkgaW4gMTUtTWludXRlIEludGVydmFscyIpICsKICB5bGFiKCJFbGVjdHJpY2l0eSBDb25zdW1wdGlvbiAoa1doKSIpCgpgYGAKCkV4YW1wbGUgZGF5IHdpdGggRVYgY2hhcmdpbmcKCmBgYHtyfQpzYW1wbGVfZGF5IDwtIGhvdXJseV9lbGVjdHJpY2l0eSAlPiUgCiAgZmlsdGVyKHNvbGFyX3llYXIgPT0gMSB8IHNvbGFyX3llYXIgPT0gMikgJT4lIAogIGZpbHRlcih5ZGF5ID09IDE1NykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIHNvbGFyX3llYXIsIHlkYXksIHRpbWUpICU+JSAKICBzdW1tYXJpemUocHJvZHVjZWRfa1doID0gbWVhbihwcm9kdWNlZF9rV2gpLCBjb25zdW1lZF9rV2ggPSBtZWFuKGNvbnN1bWVkX2tXaCksIG5ldF9rV2ggPSBtZWFuKG5ldF9rV2gpKSAlPiUgCiAgYXJyYW5nZShkYXRlLCBzb2xhcl95ZWFyLCB5ZGF5LCB0aW1lKQoKZ2dwbG90KHNhbXBsZV9kYXksIGFlcyh0aW1lLCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgI2ZhY2V0X2dyaWQocm93cyA9IHZhcnMobW9udGgpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNSkgKwogIGdndGl0bGUoIkhvbWUgRWxlY3RyaWNpdHkgaW4gMTUtTWludXRlIEludGVydmFscyIpICsKICB5bGFiKCJFbGVjdHJpY2l0eSBDb25zdW1wdGlvbiAoa1doKSIpCgpgYGAKCk9rLCBsZXQncyBkZXRlY3QgYW5kIHJlbW92ZSBFViBjaGFyZ2luZyBzZXNzaW9ucy4gCgpgYGB7cn0KIyBsaWJyYXJ5KG9ubGluZUJjcCkKeCA8LSBjKHJub3JtKDEwLCAwLCAxKSwgcm5vcm0oMTAsIDUsIDEpKQpiY3AgPC0gb25saW5lX2NwKHgpCnN1bW1hcnkoYmNwKQoKYmNwCgpgYGAKCmBgYHtyfQojIGxpYnJhcnkob25saW5lQmNwKQp4IDwtIHNhbXBsZV9kYXkgJT4lIAogIGZpbHRlcihzb2xhcl95ZWFyID09IDIpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHNlbGVjdChjb25zdW1lZF9rV2gpCgpiY3AgPC0gb25saW5lX2NwKHgpCgpzdW1tYXJ5KGJjcCkKCmJjcAoKZ2dwbG90KHNhbXBsZV9kYXksIGFlcyh0aW1lLCBjb25zdW1lZF9rV2gsIGdyb3VwPXNvbGFyX3llYXIsIGNvbG9yPXNvbGFyX3llYXIpKSArCiAgI2ZhY2V0X2dyaWQocm93cyA9IHZhcnMobW9udGgpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNSkgKwogIGdndGl0bGUoIkhvbWUgRWxlY3RyaWNpdHkgaW4gMTUtTWludXRlIEludGVydmFscyIpICsKICB5bGFiKCJFbGVjdHJpY2l0eSBDb25zdW1wdGlvbiAoa1doKSIpCmBgYAoKCg==